package com.gitee.wsl.ext.array.bytes


/**
 * Utility methods for converting byte arrays into ints and longs, and back again.
 */
object Pack {
    fun bigEndianToShort(bs: ByteArray, off: Int): Short {
        var off = off
        var n = (bs[off].toInt() and 0xff) shl 8
        n = n or (bs[++off].toInt() and 0xff)
        return n.toShort()
    }

    fun bigEndianToInt(bs: ByteArray, off: Int): Int {
        var off = off
        var n = bs[off].toInt() shl 24
        n = n or ((bs[++off].toInt() and 0xff) shl 16)
        n = n or ((bs[++off].toInt() and 0xff) shl 8)
        n = n or (bs[++off].toInt() and 0xff)
        return n
    }

    fun bigEndianToInt(bs: ByteArray, off: Int, ns: IntArray) {
        var off = off
        for (i in ns.indices) {
            ns[i] = bigEndianToInt(bs, off)
            off += 4
        }
    }

    fun bigEndianToInt(bs: ByteArray, off: Int, ns: IntArray, nsOff: Int, nsLen: Int) {
        var off = off
        for (i in 0..<nsLen) {
            ns[nsOff + i] = bigEndianToInt(bs, off)
            off += 4
        }
    }

    fun intToBigEndian(n: Int): ByteArray {
        val bs = ByteArray(4)
        intToBigEndian(n, bs, 0)
        return bs
    }

    fun intToBigEndian(n: Int, bs: ByteArray, off: Int) {
        var off = off
        bs[off] = (n ushr 24).toByte()
        bs[++off] = (n ushr 16).toByte()
        bs[++off] = (n ushr 8).toByte()
        bs[++off] = (n).toByte()
    }

    fun intToBigEndian(ns: IntArray): ByteArray {
        val bs = ByteArray(4 * ns.size)
        intToBigEndian(ns, bs, 0)
        return bs
    }

    fun intToBigEndian(ns: IntArray, bs: ByteArray, off: Int) {
        var off = off
        for (i in ns.indices) {
            intToBigEndian(ns[i], bs, off)
            off += 4
        }
    }

    fun intToBigEndian(ns: IntArray, nsOff: Int, nsLen: Int, bs: ByteArray, bsOff: Int) {
        var bsOff = bsOff
        for (i in 0..<nsLen) {
            intToBigEndian(ns[nsOff + i], bs, bsOff)
            bsOff += 4
        }
    }

    fun bigEndianToLong(bs: ByteArray, off: Int): Long {
        val hi = bigEndianToInt(bs, off)
        val lo = bigEndianToInt(bs, off + 4)
        return ((hi.toLong() and 0xffffffffL) shl 32) or (lo.toLong() and 0xffffffffL)
    }

    fun bigEndianToLong(bs: ByteArray, off: Int, ns: LongArray) {
        var off = off
        for (i in ns.indices) {
            ns[i] = bigEndianToLong(bs, off)
            off += 8
        }
    }

    fun bigEndianToLong(bs: ByteArray, bsOff: Int, ns: LongArray, nsOff: Int, nsLen: Int) {
        var bsOff = bsOff
        for (i in 0..<nsLen) {
            ns[nsOff + i] = bigEndianToLong(bs, bsOff)
            bsOff += 8
        }
    }

    fun longToBigEndian(n: Long): ByteArray {
        val bs = ByteArray(8)
        longToBigEndian(n, bs, 0)
        return bs
    }

    fun longToBigEndian(n: Long, bs: ByteArray, off: Int) {
        intToBigEndian((n ushr 32).toInt(), bs, off)
        intToBigEndian((n and 0xffffffffL).toInt(), bs, off + 4)
    }

    fun longToBigEndian(ns: LongArray): ByteArray {
        val bs = ByteArray(8 * ns.size)
        longToBigEndian(ns, bs, 0)
        return bs
    }

    fun longToBigEndian(ns: LongArray, bs: ByteArray, off: Int) {
        var off = off
        for (i in ns.indices) {
            longToBigEndian(ns[i], bs, off)
            off += 8
        }
    }

    fun longToBigEndian(ns: LongArray, nsOff: Int, nsLen: Int, bs: ByteArray, bsOff: Int) {
        var bsOff = bsOff
        for (i in 0..<nsLen) {
            longToBigEndian(ns[nsOff + i], bs, bsOff)
            bsOff += 8
        }
    }

    fun littleEndianToShort(bs: ByteArray, off: Int): Short {
        var off = off
        var n = bs[off].toInt() and 0xff
        n = n or ((bs[++off].toInt() and 0xff) shl 8)
        return n.toShort()
    }

    fun littleEndianToInt(bs: ByteArray, off: Int): Int {
        var off = off
        var n = bs[off].toInt() and 0xff
        n = n or ((bs[++off].toInt() and 0xff) shl 8)
        n = n or ((bs[++off].toInt() and 0xff) shl 16)
        n = n or (bs[++off].toInt() shl 24)
        return n
    }

    fun littleEndianToInt_High(bs: ByteArray, off: Int, len: Int): Int {
        return littleEndianToInt_Low(bs, off, len) shl ((4 - len) shl 3)
    }

    fun littleEndianToInt_Low(bs: ByteArray, off: Int, len: Int): Int {
//        assert 1 <= len && len <= 4;

        var result = bs[off].toInt() and 0xff
        var pos = 0
        for (i in 1..<len) {
            pos += 8
            result = result or ((bs[off + i].toInt() and 0xff) shl pos)
        }
        return result
    }

    fun littleEndianToInt(bs: ByteArray, off: Int, ns: IntArray) {
        var off = off
        for (i in ns.indices) {
            ns[i] = littleEndianToInt(bs, off)
            off += 4
        }
    }

    fun littleEndianToInt(bs: ByteArray, bOff: Int, ns: IntArray, nOff: Int, count: Int) {
        var bOff = bOff
        for (i in 0..<count) {
            ns[nOff + i] = littleEndianToInt(bs, bOff)
            bOff += 4
        }
    }

    fun littleEndianToInt(bs: ByteArray, off: Int, count: Int): IntArray {
        var off = off
        val ns = IntArray(count)
        for (i in ns.indices) {
            ns[i] = littleEndianToInt(bs, off)
            off += 4
        }
        return ns
    }

    fun shortToLittleEndian(n: Short): ByteArray {
        val bs = ByteArray(2)
        shortToLittleEndian(n, bs, 0)
        return bs
    }

    fun shortToLittleEndian(n: Short, bs: ByteArray, off: Int) {
        var off = off
        bs[off] = (n).toByte()
        bs[++off] = (n.toInt() ushr 8).toByte()
    }


    fun shortToBigEndian(n: Short): ByteArray {
        val r = ByteArray(2)
        shortToBigEndian(n, r, 0)
        return r
    }

    fun shortToBigEndian(n: Short, bs: ByteArray, off: Int) {
        var off = off
        bs[off] = (n.toInt() ushr 8).toByte()
        bs[++off] = (n).toByte()
    }


    fun intToLittleEndian(n: Int): ByteArray {
        val bs = ByteArray(4)
        intToLittleEndian(n, bs, 0)
        return bs
    }

    fun intToLittleEndian(n: Int, bs: ByteArray, off: Int) {
        var off = off
        bs[off] = (n).toByte()
        bs[++off] = (n ushr 8).toByte()
        bs[++off] = (n ushr 16).toByte()
        bs[++off] = (n ushr 24).toByte()
    }

    fun intToLittleEndian(ns: IntArray): ByteArray {
        val bs = ByteArray(4 * ns.size)
        intToLittleEndian(ns, bs, 0)
        return bs
    }

    fun intToLittleEndian(ns: IntArray, bs: ByteArray, off: Int) {
        var off = off
        for (i in ns.indices) {
            intToLittleEndian(ns[i], bs, off)
            off += 4
        }
    }

    fun intToLittleEndian(ns: IntArray, nsOff: Int, nsLen: Int, bs: ByteArray, bsOff: Int) {
        var bsOff = bsOff
        for (i in 0..<nsLen) {
            intToLittleEndian(ns[nsOff + i], bs, bsOff)
            bsOff += 4
        }
    }

    fun littleEndianToLong(bs: ByteArray, off: Int): Long {
        val lo = littleEndianToInt(bs, off)
        val hi = littleEndianToInt(bs, off + 4)
        return ((hi.toLong() and 0xffffffffL) shl 32) or (lo.toLong() and 0xffffffffL)
    }

    fun littleEndianToLong(bs: ByteArray, off: Int, ns: LongArray) {
        var off = off
        for (i in ns.indices) {
            ns[i] = littleEndianToLong(bs, off)
            off += 8
        }
    }

    fun littleEndianToLong(bs: ByteArray, bsOff: Int, ns: LongArray, nsOff: Int, nsLen: Int) {
        var bsOff = bsOff
        for (i in 0..<nsLen) {
            ns[nsOff + i] = littleEndianToLong(bs, bsOff)
            bsOff += 8
        }
    }

    fun longToLittleEndian_High(n: Long, bs: ByteArray, off: Int, len: Int) {
        //Debug.Assert(1 <= len && len <= 8);
        var pos = 56
        bs[off] = (n ushr pos).toByte()
        for (i in 1..<len) {
            pos -= 8
            bs[off + i] = (n ushr pos).toByte()
        }
    }

    //    public static void longToLittleEndian_Low(long n, byte[] bs, int off, int len)
    //    {
    //        longToLittleEndian_High(n << ((8 - len) << 3), bs, off, len);
    //    }
    fun littleEndianToLong_High(bs: ByteArray, off: Int, len: Int): Long {
        return littleEndianToLong_Low(bs, off, len) shl ((8 - len) shl 3)
    }

    fun littleEndianToLong_Low(bs: ByteArray, off: Int, len: Int): Long {
        //Debug.Assert(1 <= len && len <= 8);
        var result = (bs[off].toInt() and 0xFF).toLong()
        for (i in 1..<len) {
            result = result shl 8
            result = result or (bs[off + i].toInt() and 0xFF).toLong()
        }
        return result
    }

    fun longToLittleEndian(n: Long): ByteArray {
        val bs = ByteArray(8)
        longToLittleEndian(n, bs, 0)
        return bs
    }

    fun longToLittleEndian(n: Long, bs: ByteArray, off: Int) {
        intToLittleEndian((n and 0xffffffffL).toInt(), bs, off)
        intToLittleEndian((n ushr 32).toInt(), bs, off + 4)
    }

    fun longToLittleEndian(ns: LongArray): ByteArray {
        val bs = ByteArray(8 * ns.size)
        longToLittleEndian(ns, bs, 0)
        return bs
    }

    fun longToLittleEndian(ns: LongArray, bs: ByteArray, off: Int) {
        var off = off
        for (i in ns.indices) {
            longToLittleEndian(ns[i], bs, off)
            off += 8
        }
    }

    fun longToLittleEndian(ns: LongArray, nsOff: Int, nsLen: Int, bs: ByteArray, bsOff: Int) {
        var bsOff = bsOff
        for (i in 0..<nsLen) {
            longToLittleEndian(ns[nsOff + i], bs, bsOff)
            bsOff += 8
        }
    }
}
